home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume16 / less5 / part02 < prev    next >
Encoding:
Internet Message Format  |  1988-09-22  |  57.9 KB

  1. Subject:  v16i031:  Less, a pager that's more than more, Part02/04
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: ctnews!UNIX386!mark
  7. Posting-number: Volume 16, Issue 31
  8. Archive-name: less5/part02
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.
  12. # Remove anything before this line, then unpack it
  13. # by saving it into a file and typing "sh file".
  14.  
  15. echo shar: Extracting \"screen.c\"
  16. sed "s/^X//" >'screen.c' <<'END_OF_FILE'
  17. X/*
  18. X * Routines which deal with the characteristics of the terminal.
  19. X * Uses termcap to be as terminal-independent as possible.
  20. X *
  21. X * {{ Someday this should be rewritten to use curses. }}
  22. X */
  23. X
  24. X#include "less.h"
  25. X#if XENIX
  26. X#include <sys/types.h>
  27. X#include <sys/ioctl.h>
  28. X#endif
  29. X
  30. X#if TERMIO
  31. X#include <termio.h>
  32. X#else
  33. X#include <sgtty.h>
  34. X#endif
  35. X
  36. X#ifdef TIOCGWINSZ
  37. X#include <sys/ioctl.h>
  38. X#else
  39. X/*
  40. X * For the Unix PC (ATT 7300 & 3B1):
  41. X * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
  42. X * whether to include sys/window.h.  Use SIGPHONE from sys/signal.h instead.
  43. X */
  44. X#include <sys/signal.h>
  45. X#ifdef SIGPHONE
  46. X#include <sys/window.h>
  47. X#endif
  48. X#endif
  49. X
  50. X/*
  51. X * Strings passed to tputs() to do various terminal functions.
  52. X */
  53. Xstatic char
  54. X    *sc_pad,        /* Pad string */
  55. X    *sc_home,        /* Cursor home */
  56. X    *sc_addline,        /* Add line, scroll down following lines */
  57. X    *sc_lower_left,        /* Cursor to last line, first column */
  58. X    *sc_move,        /* General cursor positioning */
  59. X    *sc_clear,        /* Clear screen */
  60. X    *sc_eol_clear,        /* Clear to end of line */
  61. X    *sc_s_in,        /* Enter standout (highlighted) mode */
  62. X    *sc_s_out,        /* Exit standout mode */
  63. X    *sc_u_in,        /* Enter underline mode */
  64. X    *sc_u_out,        /* Exit underline mode */
  65. X    *sc_b_in,        /* Enter bold mode */
  66. X    *sc_b_out,        /* Exit bold mode */
  67. X    *sc_visual_bell,    /* Visual bell (flash screen) sequence */
  68. X    *sc_backspace,        /* Backspace cursor */
  69. X    *sc_init,        /* Startup terminal initialization */
  70. X    *sc_deinit;        /* Exit terminal de-intialization */
  71. X
  72. Xpublic int auto_wrap;        /* Terminal does \r\n when write past margin */
  73. Xpublic int ignaw;        /* Terminal ignores \n immediately after wrap */
  74. Xpublic int erase_char, kill_char; /* The user's erase and line-kill chars */
  75. Xpublic int sc_width, sc_height;    /* Height & width of screen */
  76. Xpublic int sc_window = -1;    /* window size for forward and backward */
  77. Xpublic int bo_width, be_width;    /* Printing width of boldface sequences */
  78. Xpublic int ul_width, ue_width;    /* Printing width of underline sequences */
  79. Xpublic int so_width, se_width;    /* Printing width of standout sequences */
  80. X
  81. X/*
  82. X * These two variables are sometimes defined in,
  83. X * and needed by, the termcap library.
  84. X * It may be necessary on some systems to declare them extern here.
  85. X */
  86. X/*extern*/ short ospeed;    /* Terminal output baud rate */
  87. X/*extern*/ char PC;        /* Pad character */
  88. X
  89. Xextern int quiet;        /* If VERY_QUIET, use visual bell for bell */
  90. Xextern int know_dumb;        /* Don't complain about a dumb terminal */
  91. Xextern int back_scroll;
  92. Xchar *tgetstr();
  93. Xchar *tgoto();
  94. X
  95. X/*
  96. X * Change terminal to "raw mode", or restore to "normal" mode.
  97. X * "Raw mode" means 
  98. X *    1. An outstanding read will complete on receipt of a single keystroke.
  99. X *    2. Input is not echoed.  
  100. X *    3. On output, \n is mapped to \r\n.
  101. X *    4. \t is NOT expanded into spaces.
  102. X *    5. Signal-causing characters such as ctrl-C (interrupt),
  103. X *       etc. are NOT disabled.
  104. X * It doesn't matter whether an input \n is mapped to \r, or vice versa.
  105. X */
  106. X    public void
  107. Xraw_mode(on)
  108. X    int on;
  109. X{
  110. X#if TERMIO
  111. X    struct termio s;
  112. X    static struct termio save_term;
  113. X
  114. X    if (on)
  115. X    {
  116. X        /*
  117. X         * Get terminal modes.
  118. X         */
  119. X        ioctl(2, TCGETA, &s);
  120. X
  121. X        /*
  122. X         * Save modes and set certain variables dependent on modes.
  123. X         */
  124. X        save_term = s;
  125. X        ospeed = s.c_cflag & CBAUD;
  126. X        erase_char = s.c_cc[VERASE];
  127. X        kill_char = s.c_cc[VKILL];
  128. X
  129. X        /*
  130. X         * Set the modes to the way we want them.
  131. X         */
  132. X        s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
  133. X        s.c_oflag |=  (OPOST|ONLCR|TAB3);
  134. X        s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
  135. X        s.c_cc[VMIN] = 1;
  136. X        s.c_cc[VTIME] = 0;
  137. X    } else
  138. X    {
  139. X        /*
  140. X         * Restore saved modes.
  141. X         */
  142. X        s = save_term;
  143. X    }
  144. X    ioctl(2, TCSETAW, &s);
  145. X#else
  146. X    struct sgttyb s;
  147. X    static struct sgttyb save_term;
  148. X
  149. X    if (on)
  150. X    {
  151. X        /*
  152. X         * Get terminal modes.
  153. X         */
  154. X        ioctl(2, TIOCGETP, &s);
  155. X
  156. X        /*
  157. X         * Save modes and set certain variables dependent on modes.
  158. X         */
  159. X        save_term = s;
  160. X        ospeed = s.sg_ospeed;
  161. X        erase_char = s.sg_erase;
  162. X        kill_char = s.sg_kill;
  163. X
  164. X        /*
  165. X         * Set the modes to the way we want them.
  166. X         */
  167. X        s.sg_flags |= CBREAK;
  168. X        s.sg_flags &= ~(ECHO|XTABS);
  169. X    } else
  170. X    {
  171. X        /*
  172. X         * Restore saved modes.
  173. X         */
  174. X        s = save_term;
  175. X    }
  176. X    ioctl(2, TIOCSETN, &s);
  177. X#endif
  178. X}
  179. X
  180. X    static void
  181. Xcannot(s)
  182. X    char *s;
  183. X{
  184. X    char message[100];
  185. X
  186. X    if (know_dumb)
  187. X        /* 
  188. X         * He knows he has a dumb terminal, so don't tell him. 
  189. X         */
  190. X        return;
  191. X
  192. X    sprintf(message, "WARNING: terminal cannot \"%s\"", s);
  193. X    error(message);
  194. X}
  195. X
  196. X/*
  197. X * Get terminal capabilities via termcap.
  198. X */
  199. X    public void
  200. Xget_term()
  201. X{
  202. X    char termbuf[2048];
  203. X    char *sp;
  204. X    char *term;
  205. X    int hard;
  206. X#ifdef TIOCGWINSZ
  207. X    struct winsize w;
  208. X#else
  209. X#ifdef WIOCGETD
  210. X    struct uwdata w;
  211. X#endif
  212. X#endif
  213. X    static char sbuf[1024];
  214. X
  215. X    char *getenv();
  216. X
  217. X    /*
  218. X     * Find out what kind of terminal this is.
  219. X     */
  220. X     if ((term = getenv("TERM")) == NULL)
  221. X         term = "unknown";
  222. X     if (tgetent(termbuf, term) <= 0)
  223. X         strcpy(termbuf, "dumb:co#80:hc:");
  224. X
  225. X    /*
  226. X     * Get size of the screen.
  227. X     */
  228. X#ifdef TIOCGWINSZ
  229. X    if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
  230. X        sc_height = w.ws_row;
  231. X    else
  232. X#else
  233. X#ifdef WIOCGETD
  234. X    if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
  235. X        sc_height = w.uw_height/w.uw_vs;
  236. X    else
  237. X#endif
  238. X#endif
  239. X         sc_height = tgetnum("li");
  240. X     hard = (sc_height < 0 || tgetflag("hc"));
  241. X    if (hard)
  242. X    {
  243. X        /* Oh no, this is a hardcopy terminal. */
  244. X        sc_height = 24;
  245. X    }
  246. X
  247. X#ifdef TIOCGWINSZ
  248. X     if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
  249. X        sc_width = w.ws_col;
  250. X    else
  251. X#ifdef WIOCGETD
  252. X    if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
  253. X        sc_width = w.uw_width/w.uw_hs;
  254. X    else
  255. X#endif
  256. X#endif
  257. X         sc_width = tgetnum("co");
  258. X     if (sc_width < 0)
  259. X          sc_width = 80;
  260. X
  261. X    auto_wrap = tgetflag("am");
  262. X    ignaw = tgetflag("xn");
  263. X
  264. X    /*
  265. X     * Assumes termcap variable "sg" is the printing width of
  266. X     * the standout sequence, the end standout sequence,
  267. X     * the underline sequence, the end underline sequence,
  268. X     * the boldface sequence, and the end boldface sequence.
  269. X     */
  270. X    if ((so_width = tgetnum("sg")) < 0)
  271. X        so_width = 0;
  272. X    be_width = bo_width = ue_width = ul_width = se_width = so_width;
  273. X
  274. X    /*
  275. X     * Get various string-valued capabilities.
  276. X     */
  277. X    sp = sbuf;
  278. X
  279. X    sc_pad = tgetstr("pc", &sp);
  280. X    if (sc_pad != NULL)
  281. X        PC = *sc_pad;
  282. X
  283. X    sc_init = tgetstr("ti", &sp);
  284. X    if (sc_init == NULL)
  285. X        sc_init = "";
  286. X
  287. X    sc_deinit= tgetstr("te", &sp);
  288. X    if (sc_deinit == NULL)
  289. X        sc_deinit = "";
  290. X
  291. X    sc_eol_clear = tgetstr("ce", &sp);
  292. X    if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
  293. X    {
  294. X        cannot("clear to end of line");
  295. X        sc_eol_clear = "";
  296. X    }
  297. X
  298. X    sc_clear = tgetstr("cl", &sp);
  299. X    if (hard || sc_clear == NULL || *sc_clear == '\0')
  300. X    {
  301. X        cannot("clear screen");
  302. X        sc_clear = "\n\n";
  303. X    }
  304. X
  305. X    sc_move = tgetstr("cm", &sp);
  306. X    if (hard || sc_move == NULL || *sc_move == '\0')
  307. X    {
  308. X        /*
  309. X         * This is not an error here, because we don't 
  310. X         * always need sc_move.
  311. X         * We need it only if we don't have home or lower-left.
  312. X         */
  313. X        sc_move = "";
  314. X    }
  315. X
  316. X    sc_s_in = tgetstr("so", &sp);
  317. X    if (hard || sc_s_in == NULL)
  318. X        sc_s_in = "";
  319. X
  320. X    sc_s_out = tgetstr("se", &sp);
  321. X    if (hard || sc_s_out == NULL)
  322. X        sc_s_out = "";
  323. X
  324. X    sc_u_in = tgetstr("us", &sp);
  325. X    if (hard || sc_u_in == NULL)
  326. X        sc_u_in = sc_s_in;
  327. X
  328. X    sc_u_out = tgetstr("ue", &sp);
  329. X    if (hard || sc_u_out == NULL)
  330. X        sc_u_out = sc_s_out;
  331. X
  332. X    sc_b_in = tgetstr("md", &sp);
  333. X    if (hard || sc_b_in == NULL)
  334. X    {
  335. X        sc_b_in = sc_s_in;
  336. X        sc_b_out = sc_s_out;
  337. X    } else
  338. X    {
  339. X        sc_b_out = tgetstr("me", &sp);
  340. X        if (hard || sc_b_out == NULL)
  341. X            sc_b_out = "";
  342. X    }
  343. X
  344. X    sc_visual_bell = tgetstr("vb", &sp);
  345. X    if (hard || sc_visual_bell == NULL)
  346. X        sc_visual_bell = "";
  347. X
  348. X    sc_home = tgetstr("ho", &sp);
  349. X    if (hard || sc_home == NULL || *sc_home == '\0')
  350. X    {
  351. X        if (*sc_move == '\0')
  352. X        {
  353. X            cannot("home cursor");
  354. X            /*
  355. X             * This last resort for sc_home is supposed to
  356. X             * be an up-arrow suggesting moving to the 
  357. X             * top of the "virtual screen". (The one in
  358. X             * your imagination as you try to use this on
  359. X             * a hard copy terminal.)
  360. X             */
  361. X            sc_home = "|\b^";        
  362. X        } else
  363. X        {
  364. X            /* 
  365. X             * No "home" string,
  366. X             * but we can use "move(0,0)".
  367. X             */
  368. X            strcpy(sp, tgoto(sc_move, 0, 0));
  369. X            sc_home = sp;
  370. X            sp += strlen(sp) + 1;
  371. X        }
  372. X    }
  373. X
  374. X    sc_lower_left = tgetstr("ll", &sp);
  375. X    if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
  376. X    {
  377. X        if (*sc_move == '\0')
  378. X        {
  379. X            cannot("move cursor to lower left of screen");
  380. X            sc_lower_left = "\r";
  381. X        } else
  382. X        {
  383. X            /*
  384. X             * No "lower-left" string, 
  385. X             * but we can use "move(0,last-line)".
  386. X             */
  387. X            strcpy(sp, tgoto(sc_move, 0, sc_height-1));
  388. X            sc_lower_left = sp;
  389. X            sp += strlen(sp) + 1;
  390. X        }
  391. X    }
  392. X
  393. X    /*
  394. X     * To add a line at top of screen and scroll the display down,
  395. X     * we use "al" (add line) or "sr" (scroll reverse).
  396. X     */
  397. X    if ((sc_addline = tgetstr("al", &sp)) == NULL || 
  398. X         *sc_addline == '\0')
  399. X        sc_addline = tgetstr("sr", &sp);
  400. X
  401. X    if (hard || sc_addline == NULL || *sc_addline == '\0')
  402. X    {
  403. X        cannot("scroll backwards");
  404. X        sc_addline = "";
  405. X        /* Force repaint on any backward movement */
  406. X        back_scroll = 0;
  407. X    }
  408. X
  409. X    if (tgetflag("bs"))
  410. X        sc_backspace = "\b";
  411. X    else
  412. X    {
  413. X        sc_backspace = tgetstr("bc", &sp);
  414. X        if (sc_backspace == NULL || *sc_backspace == '\0')
  415. X            sc_backspace = "\b";
  416. X    }
  417. X}
  418. X
  419. X
  420. X/*
  421. X * Below are the functions which perform all the 
  422. X * terminal-specific screen manipulation.
  423. X */
  424. X
  425. X
  426. X/*
  427. X * Initialize terminal
  428. X */
  429. X    public void
  430. Xinit()
  431. X{
  432. X    tputs(sc_init, sc_height, putchr);
  433. X}
  434. X
  435. X/*
  436. X * Deinitialize terminal
  437. X */
  438. X    public void
  439. Xdeinit()
  440. X{
  441. X    tputs(sc_deinit, sc_height, putchr);
  442. X}
  443. X
  444. X/*
  445. X * Home cursor (move to upper left corner of screen).
  446. X */
  447. X    public void
  448. Xhome()
  449. X{
  450. X    tputs(sc_home, 1, putchr);
  451. X}
  452. X
  453. X/*
  454. X * Add a blank line (called with cursor at home).
  455. X * Should scroll the display down.
  456. X */
  457. X    public void
  458. Xadd_line()
  459. X{
  460. X    tputs(sc_addline, sc_height, putchr);
  461. X}
  462. X
  463. X/*
  464. X * Move cursor to lower left corner of screen.
  465. X */
  466. X    public void
  467. Xlower_left()
  468. X{
  469. X    tputs(sc_lower_left, 1, putchr);
  470. X}
  471. X
  472. X/*
  473. X * Ring the terminal bell.
  474. X */
  475. X    public void
  476. Xbell()
  477. X{
  478. X    if (quiet == VERY_QUIET)
  479. X        vbell();
  480. X    else
  481. X        putchr('\7');
  482. X}
  483. X
  484. X/*
  485. X * Output the "visual bell", if there is one.
  486. X */
  487. X    public void
  488. Xvbell()
  489. X{
  490. X    if (*sc_visual_bell == '\0')
  491. X        return;
  492. X    tputs(sc_visual_bell, sc_height, putchr);
  493. X}
  494. X
  495. X/*
  496. X * Clear the screen.
  497. X */
  498. X    public void
  499. Xclear()
  500. X{
  501. X    tputs(sc_clear, sc_height, putchr);
  502. X}
  503. X
  504. X/*
  505. X * Clear from the cursor to the end of the cursor's line.
  506. X * {{ This must not move the cursor. }}
  507. X */
  508. X    public void
  509. Xclear_eol()
  510. X{
  511. X    tputs(sc_eol_clear, 1, putchr);
  512. X}
  513. X
  514. X/*
  515. X * Begin "standout" (bold, underline, or whatever).
  516. X */
  517. X    public void
  518. Xso_enter()
  519. X{
  520. X    tputs(sc_s_in, 1, putchr);
  521. X}
  522. X
  523. X/*
  524. X * End "standout".
  525. X */
  526. X    public void
  527. Xso_exit()
  528. X{
  529. X    tputs(sc_s_out, 1, putchr);
  530. X}
  531. X
  532. X/*
  533. X * Begin "underline" (hopefully real underlining, 
  534. X * otherwise whatever the terminal provides).
  535. X */
  536. X    public void
  537. Xul_enter()
  538. X{
  539. X    tputs(sc_u_in, 1, putchr);
  540. X}
  541. X
  542. X/*
  543. X * End "underline".
  544. X */
  545. X    public void
  546. Xul_exit()
  547. X{
  548. X    tputs(sc_u_out, 1, putchr);
  549. X}
  550. X
  551. X/*
  552. X * Begin "bold"
  553. X */
  554. X    public void
  555. Xbo_enter()
  556. X{
  557. X    tputs(sc_b_in, 1, putchr);
  558. X}
  559. X
  560. X/*
  561. X * End "bold".
  562. X */
  563. X    public void
  564. Xbo_exit()
  565. X{
  566. X    tputs(sc_b_out, 1, putchr);
  567. X}
  568. X
  569. X/*
  570. X * Erase the character to the left of the cursor 
  571. X * and move the cursor left.
  572. X */
  573. X    public void
  574. Xbackspace()
  575. X{
  576. X    /* 
  577. X     * Try to erase the previous character by overstriking with a space.
  578. X     */
  579. X    tputs(sc_backspace, 1, putchr);
  580. X    putchr(' ');
  581. X    tputs(sc_backspace, 1, putchr);
  582. X}
  583. X
  584. X/*
  585. X * Output a plain backspace, without erasing the previous char.
  586. X */
  587. X    public void
  588. Xputbs()
  589. X{
  590. X    tputs(sc_backspace, 1, putchr);
  591. X}
  592. END_OF_FILE
  593. echo shar: Extracting \"prompt.c\"
  594. sed "s/^X//" >'prompt.c' <<'END_OF_FILE'
  595. X/*
  596. X * Prompting and other messages.
  597. X * There are three flavors of prompts, SHORT, MEDIUM and LONG,
  598. X * selected by the -m/-M options.
  599. X * There is also the "equals message", printed by the = command.
  600. X * A prompt is a message composed of various pieces, such as the 
  601. X * name of the file being viewed, the percentage into the file, etc.
  602. X */
  603. X
  604. X#include "less.h"
  605. X#include "position.h"
  606. X
  607. Xextern int pr_type;
  608. Xextern int ispipe;
  609. Xextern int hit_eof;
  610. Xextern int new_file;
  611. Xextern int sc_width;
  612. Xextern int so_width, se_width;
  613. Xextern char *current_file;
  614. Xextern int ac;
  615. Xextern char **av;
  616. Xextern int curr_ac;
  617. Xextern int linenums;
  618. X
  619. X/*
  620. X * Prototypes for the three flavors of prompts.
  621. X * These strings are expanded by pr_expand().
  622. X */
  623. Xstatic char s_proto[] =
  624. X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t";
  625. Xstatic char m_proto[] =
  626. X  "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t";
  627. Xstatic char M_proto[] =
  628. X  "?f%f .?n?m(file %i of %m) ..?ltline %lt :byte %bB?s/%s ..?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t";
  629. Xstatic char e_proto[] =
  630. X  "?f%f .?m(file %i of %m) .?ltline %lt .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t";
  631. X
  632. Xchar *prproto[3];
  633. Xchar *eqproto = e_proto;
  634. X
  635. Xstatic char message[250];
  636. Xstatic char *mp;
  637. X
  638. X/*
  639. X * Initialize the prompt prototype strings.
  640. X */
  641. X    public void
  642. Xinit_prompt()
  643. X{
  644. X    prproto[0] = save(s_proto);
  645. X    prproto[1] = save(m_proto);
  646. X    prproto[2] = save(M_proto);
  647. X    eqproto = save(e_proto);
  648. X}
  649. X
  650. X/*
  651. X * Set the message pointer to the end of the message string.
  652. X */
  653. X    static void
  654. Xsetmp()
  655. X{
  656. X    while (*mp != '\0')
  657. X        mp++;
  658. X}
  659. X
  660. X/*
  661. X * Append a POSITION (as a decimal integer) to the end of the message.
  662. X */
  663. X    static void
  664. Xap_pos(pos)
  665. X    POSITION pos;
  666. X{
  667. X    sprintf(mp, "%ld", (long)pos);
  668. X    setmp();
  669. X}
  670. X
  671. X/*
  672. X * Append an integer to the end of the message.
  673. X */
  674. X    static void
  675. Xap_int(n)
  676. X    int n;
  677. X{
  678. X    sprintf(mp, "%d", n);
  679. X    setmp();
  680. X}
  681. X
  682. X/*
  683. X * Append a question mark to the end of the message.
  684. X */
  685. X    static void
  686. Xap_quest()
  687. X{
  688. X    *mp++ = '?';
  689. X}
  690. X
  691. X/*
  692. X * Return the "current" byte offset in the file.
  693. X */
  694. X    static POSITION
  695. Xcurr_byte(where)
  696. X    int where;
  697. X{
  698. X    POSITION pos;
  699. X
  700. X    pos = position(where);
  701. X    if (pos == NULL_POSITION)
  702. X        pos = ch_length();
  703. X    return (pos);
  704. X}
  705. X
  706. X/*
  707. X * Return the value of a prototype conditional.
  708. X * A prototype string may include conditionals which consist of a 
  709. X * question mark followed by a single letter.
  710. X * Here we decode that letter and return the appropriate boolean value.
  711. X */
  712. X    static int
  713. Xcond(c, where)
  714. X    char c;
  715. X    int where;
  716. X{
  717. X    switch (c)
  718. X    {
  719. X    case 'a':    /* Anything in the message yet? */
  720. X        return (mp > message);
  721. X    case 'b':    /* Current byte offset known? */
  722. X        return (curr_byte(where) != NULL_POSITION);
  723. X    case 'e':    /* At end of file? */
  724. X        return (hit_eof);
  725. X    case 'f':    /* Filename known? */
  726. X        return (!ispipe);
  727. X    case 'l':    /* Line number known? */
  728. X        return (linenums);
  729. X    case 'm':    /* More than one file? */
  730. X        return (ac > 1);
  731. X    case 'n':    /* First prompt in a new file? */
  732. X        return (new_file);
  733. X    case 'p':    /* Percent into file known? */
  734. X        return (curr_byte(where) != NULL_POSITION && 
  735. X                ch_length() > 0);
  736. X    case 's':    /* Size of file known? */
  737. X        return (ch_length() != NULL_POSITION);
  738. X    case 'x':    /* Is there a "next" file? */
  739. X        return (curr_ac + 1 < ac);
  740. X    }
  741. X    return (0);
  742. X}
  743. X
  744. X/*
  745. X * Decode a "percent" prototype character.
  746. X * A prototype string may include various "percent" escapes;
  747. X * that is, a percent sign followed by a single letter.
  748. X * Here we decode that letter and take the appropriate action,
  749. X * usually by appending something to the message being built.
  750. X */
  751. X    static void
  752. Xprotochar(c, where)
  753. X    int c;
  754. X    int where;
  755. X{
  756. X    POSITION pos;
  757. X    POSITION len;
  758. X    int n;
  759. X
  760. X    switch (c)
  761. X    {
  762. X    case 'b':    /* Current byte offset */
  763. X        pos = curr_byte(where);
  764. X        if (pos != NULL_POSITION)
  765. X            ap_pos(pos);
  766. X        else
  767. X            ap_quest();
  768. X        break;
  769. X    case 'f':    /* File name */
  770. X        strtcpy(mp, current_file,
  771. X            (unsigned int)(&message[sizeof(message)] - mp));
  772. X        setmp();
  773. X        break;
  774. X    case 'i':    /* Index into list of files */
  775. X        ap_int(curr_ac + 1);
  776. X        break;
  777. X    case 'l':    /* Current line number */
  778. X        n = currline(where);
  779. X        if (n != 0)
  780. X            ap_int(n);
  781. X        else
  782. X            ap_quest();
  783. X        break;
  784. X    case 'm':    /* Number of files */
  785. X        ap_int(ac);
  786. X        break;
  787. X    case 'p':    /* Percent into file */
  788. X        pos = curr_byte(where);
  789. X        len = ch_length();
  790. X        if (pos != NULL_POSITION && len > 0)
  791. X            ap_int((int)(100*pos / len));
  792. X        else
  793. X            ap_quest();
  794. X        break;
  795. X    case 's':    /* Size of file */
  796. X        len = ch_length();
  797. X        if (len != NULL_POSITION)
  798. X            ap_pos(len);
  799. X        else
  800. X            ap_quest();
  801. X        break;
  802. X    case 't':    /* Truncate trailing spaces in the message */
  803. X        while (mp > message && mp[-1] == ' ')
  804. X            mp--;
  805. X        break;
  806. X    case 'x':    /* Name of next file */
  807. X        if (curr_ac + 1 < ac)
  808. X        {
  809. X            strtcpy(mp, av[curr_ac+1],
  810. X                (unsigned int)(&message[sizeof(message)] - mp));
  811. X            setmp();
  812. X        } else
  813. X            ap_quest();
  814. X        break;
  815. X    }
  816. X}
  817. X
  818. X/*
  819. X * Skip a false conditional.
  820. X * When a false condition is found (either a false IF or the ELSE part 
  821. X * of a true IF), this routine scans the prototype string to decide
  822. X * where to resume parsing the string.
  823. X * We must keep track of nested IFs and skip them properly.
  824. X */
  825. X    static char *
  826. Xskipcond(p)
  827. X    register char *p;
  828. X{
  829. X    register int iflevel = 1;
  830. X
  831. X    for (;;) switch (*++p)
  832. X    {
  833. X    case '?':
  834. X        /*
  835. X         * Start of a nested IF.
  836. X         */
  837. X        iflevel++;
  838. X        break;
  839. X    case ':':
  840. X        /*
  841. X         * Else.
  842. X         * If this matches the IF we came in here with,
  843. X         * then we're done.
  844. X         */
  845. X        if (iflevel == 1)
  846. X            return (p);
  847. X        break;
  848. X    case '.':
  849. X        /*
  850. X         * Endif.
  851. X         * If this matches the IF we came in here with,
  852. X         * then we're done.
  853. X         */
  854. X        if (--iflevel == 0)
  855. X            return (p);
  856. X        break;
  857. X    case '\\':
  858. X        /*
  859. X         * Backslash escapes the next character.
  860. X         */
  861. X        ++p;
  862. X        break;
  863. X    case '\0':
  864. X        /*
  865. X         * Whoops.  Hit end of string.
  866. X         * This is a malformed conditional, but just treat it
  867. X         * as if all active conditionals ends here.
  868. X         */
  869. X        return (p-1);
  870. X    }
  871. X    /*NOTREACHED*/
  872. X}
  873. X
  874. X    static char *
  875. Xwherechar(p, wp)
  876. X    char *p;
  877. X    int *wp;
  878. X{
  879. X    int c;
  880. X
  881. X    switch (c = *p)
  882. X    {
  883. X    case 'b': case 'l': case 'p':
  884. X        switch (*++p)
  885. X        {
  886. X        case 't':   *wp = TOP;            break;
  887. X        case 'm':   *wp = MIDDLE;        break;
  888. X        case 'b':   *wp = BOTTOM;        break;
  889. X        case 'B':   *wp = BOTTOM_PLUS_ONE;    break;
  890. X        default:    *wp = TOP;            break;
  891. X        }
  892. X    }
  893. X    return (p);
  894. X}
  895. X
  896. X/*
  897. X * Construct a message based on a prototype string.
  898. X */
  899. X    static char *
  900. Xpr_expand(proto, maxwidth)
  901. X    char *proto;
  902. X    int maxwidth;
  903. X{
  904. X    register char *p;
  905. X    register int c;
  906. X    int where;
  907. X
  908. X    mp = message;
  909. X
  910. X    if (*proto == '\0')
  911. X        return ("");
  912. X
  913. X    for (p = proto;  *p != '\0';  p++)
  914. X    {
  915. X        switch (*p)
  916. X        {
  917. X        default:    /* Just put the character in the message */
  918. X            *mp++ = *p;
  919. X            break;
  920. X        case '\\':    /* Backslash escapes the next character */
  921. X            p++;
  922. X            *mp++ = *p;
  923. X            break;
  924. X        case '?':    /* Conditional (IF) */
  925. X            if ((c = *++p) == '\0')
  926. X                --p;
  927. X            else
  928. X            {
  929. X                p = wherechar(p, &where);
  930. X                if (!cond(c, where))
  931. X                    p = skipcond(p);
  932. X            }
  933. X            break;
  934. X        case ':':    /* ELSE */
  935. X            p = skipcond(p);
  936. X            break;
  937. X        case '.':    /* ENDIF */
  938. X            break;
  939. X        case '%':    /* Percent escape */
  940. X            if ((c = *++p) == '\0')
  941. X                --p;
  942. X            else
  943. X            {
  944. X                p = wherechar(p, &where);
  945. X                protochar(c, where);
  946. X            }
  947. X            break;
  948. X        }
  949. X    }
  950. X
  951. X    new_file = 0;
  952. X    if (mp == message)
  953. X        return (NULL);
  954. X    *mp = '\0';
  955. X    if (maxwidth > 0 && mp >= message + maxwidth)
  956. X    {
  957. X        /*
  958. X         * Message is too long.
  959. X         * Return just the final portion of it.
  960. X         */
  961. X        return (mp - maxwidth);
  962. X    }
  963. X    return (message);
  964. X}
  965. X
  966. X/*
  967. X * Return a message suitable for printing by the "=" command.
  968. X */
  969. X    public char *
  970. Xeq_message()
  971. X{
  972. X    return (pr_expand(eqproto, 0));
  973. X}
  974. X
  975. X/*
  976. X * Return a prompt.
  977. X * This depends on the prompt type (SHORT, MEDIUM, LONG), etc.
  978. X * If we can't come up with an appropriate prompt, return NULL
  979. X * and the caller will prompt with a colon.
  980. X */
  981. X    public char *
  982. Xpr_string()
  983. X{
  984. X    return (pr_expand(prproto[pr_type], sc_width-so_width-se_width-2));
  985. X}
  986. END_OF_FILE
  987. echo shar: Extracting \"line.c\"
  988. sed "s/^X//" >'line.c' <<'END_OF_FILE'
  989. X/*
  990. X * Routines to manipulate the "line buffer".
  991. X * The line buffer holds a line of output as it is being built
  992. X * in preparation for output to the screen.
  993. X * We keep track of the PRINTABLE length of the line as it is being built.
  994. X */
  995. X
  996. X#include "less.h"
  997. X
  998. Xstatic char linebuf[1024];    /* Buffer which holds the current output line */
  999. Xstatic char *curr;        /* Pointer into linebuf */
  1000. Xstatic int column;        /* Printable length, accounting for
  1001. X                   backspaces, etc. */
  1002. X/*
  1003. X * A ridiculously complex state machine takes care of backspaces 
  1004. X * when in BS_SPECIAL mode.  The complexity arises from the attempt
  1005. X * to deal with all cases, especially involving long lines with underlining,
  1006. X * boldfacing or whatever.  There are still some cases which will break it.
  1007. X *
  1008. X * There are four states:
  1009. X *    LN_NORMAL is the normal state (not in underline mode).
  1010. X *    LN_UNDERLINE means we are in underline mode.  We expect to get
  1011. X *        either a sequence like "_\bX" or "X\b_" to continue
  1012. X *        underline mode, or anything else to end underline mode.
  1013. X *    LN_BOLDFACE means we are in boldface mode.  We expect to get sequences
  1014. X *        like "X\bX\b...X\bX" to continue boldface mode, or anything
  1015. X *        else to end boldface mode.
  1016. X *    LN_UL_X means we are one character after LN_UNDERLINE
  1017. X *        (we have gotten the '_' in "_\bX" or the 'X' in "X\b_").
  1018. X *    LN_UL_XB means we are one character after LN_UL_X 
  1019. X *        (we have gotten the backspace in "_\bX" or "X\b_";
  1020. X *        we expect one more ordinary character, 
  1021. X *        which will put us back in state LN_UNDERLINE).
  1022. X *    LN_BO_X means we are one character after LN_BOLDFACE
  1023. X *        (we have gotten the 'X' in "X\bX").
  1024. X *    LN_BO_XB means we are one character after LN_BO_X
  1025. X *        (we have gotten the backspace in "X\bX";
  1026. X *        we expect one more 'X' which will put us back
  1027. X *        in LN_BOLDFACE).
  1028. X */
  1029. Xstatic int ln_state;        /* Currently in normal/underline/bold/etc mode? */
  1030. X#define    LN_NORMAL    0    /* Not in underline, boldface or whatever mode */
  1031. X#define    LN_UNDERLINE    1    /* In underline, need next char */
  1032. X#define    LN_UL_X        2    /* In underline, got char, need \b */
  1033. X#define    LN_UL_XB    3    /* In underline, got char & \b, need one more */
  1034. X#define    LN_BOLDFACE    4    /* In boldface, need next char */
  1035. X#define    LN_BO_X        5    /* In boldface, got char, need \b */
  1036. X#define    LN_BO_XB    6    /* In boldface, got char & \b, need same char */
  1037. X
  1038. Xpublic char *line;        /* Pointer to the current line.
  1039. X                   Usually points to linebuf. */
  1040. X
  1041. Xextern int bs_mode;
  1042. Xextern int tabstop;
  1043. Xextern int bo_width, be_width;
  1044. Xextern int ul_width, ue_width;
  1045. Xextern int sc_width, sc_height;
  1046. X
  1047. X/*
  1048. X * Rewind the line buffer.
  1049. X */
  1050. X    public void
  1051. Xprewind()
  1052. X{
  1053. X    line = curr = linebuf;
  1054. X    ln_state = LN_NORMAL;
  1055. X    column = 0;
  1056. X}
  1057. X
  1058. X/*
  1059. X * Append a character to the line buffer.
  1060. X * Expand tabs into spaces, handle underlining, boldfacing, etc.
  1061. X * Returns 0 if ok, 1 if couldn't fit in buffer.
  1062. X */
  1063. X
  1064. X#define    NEW_COLUMN(newcol)    if ((newcol) + ((ln_state)?ue_width:0) > sc_width) \
  1065. X                    return (1); else column = (newcol)
  1066. X
  1067. X    public int
  1068. Xpappend(c)
  1069. X    int c;
  1070. X{
  1071. X    if (c == '\0')
  1072. X    {
  1073. X        /*
  1074. X         * Terminate any special modes, if necessary.
  1075. X         * Append a '\0' to the end of the line.
  1076. X         */
  1077. X        switch (ln_state)
  1078. X        {
  1079. X        case LN_UL_X:
  1080. X            curr[0] = curr[-1];
  1081. X            curr[-1] = UE_CHAR;
  1082. X            curr++;
  1083. X            break;
  1084. X        case LN_BO_X:
  1085. X            curr[0] = curr[-1];
  1086. X            curr[-1] = BE_CHAR;
  1087. X            curr++;
  1088. X            break;
  1089. X        case LN_UL_XB:
  1090. X        case LN_UNDERLINE:
  1091. X            *curr++ = UE_CHAR;
  1092. X            break;
  1093. X        case LN_BO_XB:
  1094. X        case LN_BOLDFACE:
  1095. X            *curr++ = BE_CHAR;
  1096. X            break;
  1097. X        }
  1098. X        ln_state = LN_NORMAL;
  1099. X        *curr = '\0';
  1100. X        return (0);
  1101. X    }
  1102. X
  1103. X    if (curr > linebuf + sizeof(linebuf) - 12)
  1104. X        /*
  1105. X         * Almost out of room in the line buffer.
  1106. X         * Don't take any chances.
  1107. X         * {{ Linebuf is supposed to be big enough that this
  1108. X         *    will never happen, but may need to be made 
  1109. X         *    bigger for wide screens or lots of backspaces. }}
  1110. X         */
  1111. X        return (1);
  1112. X
  1113. X    if (bs_mode == BS_SPECIAL)
  1114. X    {
  1115. X        /*
  1116. X         * Advance the state machine.
  1117. X         */
  1118. X        switch (ln_state)
  1119. X        {
  1120. X        case LN_NORMAL:
  1121. X            if (curr <= linebuf + 1 || curr[-1] != '\b')
  1122. X                break;
  1123. X
  1124. X            if (c == curr[-2])
  1125. X                goto enter_boldface;
  1126. X            if (c == '_' || curr[-2] == '_')
  1127. X                goto enter_underline;
  1128. X            curr -= 2;
  1129. X            break;
  1130. X
  1131. Xenter_boldface:
  1132. X            /*
  1133. X             * We have "X\bX" (including the current char).
  1134. X             * Switch into boldface mode.
  1135. X             */
  1136. X            if (column + bo_width + be_width + 1 >= sc_width)
  1137. X                /*
  1138. X                 * Not enough room left on the screen to 
  1139. X                 * enter and exit boldface mode.
  1140. X                 */
  1141. X                return (1);
  1142. X
  1143. X            if (bo_width > 0 && 
  1144. X                curr > linebuf + 2 && curr[-3] == ' ')
  1145. X            {
  1146. X                /*
  1147. X                 * Special case for magic cookie terminals:
  1148. X                 * if the previous char was a space, replace 
  1149. X                 * it with the "enter boldface" sequence.
  1150. X                 */
  1151. X                curr[-3] = BO_CHAR;
  1152. X                column += bo_width-1;
  1153. X            } else
  1154. X            {
  1155. X                curr[-1] = curr[-2];
  1156. X                curr[-2] = BO_CHAR;
  1157. X                column += bo_width;
  1158. X                curr++;
  1159. X            }
  1160. X            goto ln_bo_xb_case;
  1161. X
  1162. Xenter_underline:
  1163. X            /*
  1164. X             * We have either "_\bX" or "X\b_" (including
  1165. X             * the current char).  Switch into underline mode.
  1166. X             */
  1167. X            if (column + ul_width + ue_width + 1 >= sc_width)
  1168. X                /*
  1169. X                 * Not enough room left on the screen to 
  1170. X                 * enter and exit underline mode.
  1171. X                 */
  1172. X                return (1);
  1173. X
  1174. X            if (ul_width > 0 && 
  1175. X                curr > linebuf + 2 && curr[-3] == ' ')
  1176. X            {
  1177. X                /*
  1178. X                 * Special case for magic cookie terminals:
  1179. X                 * if the previous char was a space, replace 
  1180. X                 * it with the "enter underline" sequence.
  1181. X                 */
  1182. X                curr[-3] = UL_CHAR;
  1183. X                column += ul_width-1;
  1184. X            } else
  1185. X            {
  1186. X                curr[-1] = curr[-2];
  1187. X                curr[-2] = UL_CHAR;
  1188. X                column += ul_width;
  1189. X                curr++;
  1190. X            }
  1191. X            goto ln_ul_xb_case;
  1192. X            /*NOTREACHED*/
  1193. X        case LN_UL_XB:
  1194. X            /*
  1195. X             * Termination of a sequence "_\bX" or "X\b_".
  1196. X             */
  1197. X            if (c != '_' && curr[-2] != '_' && c == curr[-2])
  1198. X            {
  1199. X                /*
  1200. X                 * We seem to have run on from underlining
  1201. X                 * into boldfacing - this is a nasty fix, but
  1202. X                 * until this whole routine is rewritten as a
  1203. X                 * real DFA, ...  well ...
  1204. X                 */
  1205. X                curr[0] = curr[-2];
  1206. X                curr[-2] = UE_CHAR;
  1207. X                curr[-1] = BO_CHAR;
  1208. X                curr += 2; /* char & non-existent backspace */
  1209. X                ln_state = LN_BO_XB;
  1210. X                goto ln_bo_xb_case;
  1211. X            }
  1212. Xln_ul_xb_case:
  1213. X            if (c == '_')
  1214. X                c = curr[-2];
  1215. X            curr -= 2;
  1216. X            ln_state = LN_UNDERLINE;
  1217. X            break;
  1218. X        case LN_BO_XB:
  1219. X            /*
  1220. X             * Termination of a sequnce "X\bX".
  1221. X             */
  1222. X            if (c != curr[-2] && (c == '_' || curr[-2] == '_'))
  1223. X            {
  1224. X                /*
  1225. X                 * We seem to have run on from
  1226. X                 * boldfacing into underlining.
  1227. X                 */
  1228. X                curr[0] = curr[-2];
  1229. X                curr[-2] = BE_CHAR;
  1230. X                curr[-1] = UL_CHAR;
  1231. X                curr += 2; /* char & non-existent backspace */
  1232. X                ln_state = LN_UL_XB;
  1233. X                goto ln_ul_xb_case;
  1234. X            }
  1235. Xln_bo_xb_case:
  1236. X            curr -= 2;
  1237. X            ln_state = LN_BOLDFACE;
  1238. X            break;
  1239. X        case LN_UNDERLINE:
  1240. X            if (column + ue_width + bo_width + 1 + be_width >= sc_width)
  1241. X                /*
  1242. X                 * We have just barely enough room to 
  1243. X                 * exit underline mode and handle a possible
  1244. X                 * underline/boldface run on mixup.
  1245. X                 */
  1246. X                return (1);
  1247. X            ln_state = LN_UL_X;
  1248. X            break;
  1249. X        case LN_BOLDFACE:
  1250. X            if (c == '\b')
  1251. X            {
  1252. X                ln_state = LN_BO_XB;
  1253. X                break;
  1254. X            }
  1255. X            if (column + be_width + ul_width + 1 + ue_width >= sc_width)
  1256. X                /*
  1257. X                 * We have just barely enough room to 
  1258. X                 * exit underline mode and handle a possible
  1259. X                 * underline/boldface run on mixup.
  1260. X                 */
  1261. X                return (1);
  1262. X            ln_state = LN_BO_X;
  1263. X            break;
  1264. X        case LN_UL_X:
  1265. X            if (c == '\b')
  1266. X                ln_state = LN_UL_XB;
  1267. X            else
  1268. X            {
  1269. X                /*
  1270. X                 * Exit underline mode.
  1271. X                 * We have to shuffle the chars a bit
  1272. X                 * to make this work.
  1273. X                 */
  1274. X                curr[0] = curr[-1];
  1275. X                curr[-1] = UE_CHAR;
  1276. X                column += ue_width;
  1277. X                if (ue_width > 0 && curr[0] == ' ')
  1278. X                    /*
  1279. X                     * Another special case for magic
  1280. X                     * cookie terminals: if the next
  1281. X                     * char is a space, replace it
  1282. X                     * with the "exit underline" sequence.
  1283. X                     */
  1284. X                    column--;
  1285. X                else
  1286. X                    curr++;
  1287. X                ln_state = LN_NORMAL;
  1288. X            } 
  1289. X            break;
  1290. X        case LN_BO_X:
  1291. X            if (c == '\b')
  1292. X                ln_state = LN_BO_XB;
  1293. X            else
  1294. X            {
  1295. X                /*
  1296. X                 * Exit boldface mode.
  1297. X                 * We have to shuffle the chars a bit
  1298. X                 * to make this work.
  1299. X                 */
  1300. X                curr[0] = curr[-1];
  1301. X                curr[-1] = BE_CHAR;
  1302. X                column += be_width;
  1303. X                if (be_width > 0 && curr[0] == ' ')
  1304. X                    /*
  1305. X                     * Another special case for magic
  1306. X                     * cookie terminals: if the next
  1307. X                     * char is a space, replace it
  1308. X                     * with the "exit boldface" sequence.
  1309. X                     */
  1310. X                    column--;
  1311. X                else
  1312. X                    curr++;
  1313. X                ln_state = LN_NORMAL;
  1314. X            } 
  1315. X            break;
  1316. X        }
  1317. X    }
  1318. X    
  1319. X    if (c == '\t') 
  1320. X    {
  1321. X        /*
  1322. X         * Expand a tab into spaces.
  1323. X         */
  1324. X        do
  1325. X        {
  1326. X            NEW_COLUMN(column+1);
  1327. X        } while ((column % tabstop) != 0);
  1328. X        *curr++ = '\t';
  1329. X        return (0);
  1330. X    }
  1331. X
  1332. X    if (c == '\b')
  1333. X    {
  1334. X        if (bs_mode == BS_CONTROL)
  1335. X        {
  1336. X            /*
  1337. X             * Treat backspace as a control char: output "^H".
  1338. X             */
  1339. X            NEW_COLUMN(column+2);
  1340. X            *curr++ = ('H' | 0200);
  1341. X        } else
  1342. X        {
  1343. X            /*
  1344. X             * Output a real backspace.
  1345. X             */
  1346. X            column--;
  1347. X            *curr++ = '\b';
  1348. X        }
  1349. X        return (0);
  1350. X    } 
  1351. X
  1352. X    if (control_char(c))
  1353. X    {
  1354. X        /*
  1355. X         * Put a "^X" into the buffer.
  1356. X         * The 0200 bit is used to tell put_line() to prefix
  1357. X         * the char with a ^.  We don't actually put the ^
  1358. X         * in the buffer because we sometimes need to move
  1359. X         * chars around, and such movement might separate 
  1360. X         * the ^ from its following character.
  1361. X         * {{ This should be redone so that we can use an
  1362. X         *    8 bit (e.g. international) character set. }}
  1363. X         */
  1364. X        NEW_COLUMN(column+2);
  1365. X        *curr++ = (carat_char(c) | 0200);
  1366. X        return (0);
  1367. X    }
  1368. X
  1369. X    /*
  1370. X     * Ordinary character.  Just put it in the buffer.
  1371. X     */
  1372. X    NEW_COLUMN(column+1);
  1373. X    *curr++ = c;
  1374. X    return (0);
  1375. X}
  1376. X
  1377. X/*
  1378. X * Analogous to forw_line(), but deals with "raw lines":
  1379. X * lines which are not split for screen width.
  1380. X * {{ This is supposed to be more efficient than forw_line(). }}
  1381. X */
  1382. X    public POSITION
  1383. Xforw_raw_line(curr_pos)
  1384. X    POSITION curr_pos;
  1385. X{
  1386. X    register char *p;
  1387. X    register int c;
  1388. X    POSITION new_pos;
  1389. X
  1390. X    if (curr_pos == NULL_POSITION || ch_seek(curr_pos) ||
  1391. X        (c = ch_forw_get()) == EOI)
  1392. X        return (NULL_POSITION);
  1393. X
  1394. X    p = linebuf;
  1395. X
  1396. X    for (;;)
  1397. X    {
  1398. X        if (c == '\n' || c == EOI)
  1399. X        {
  1400. X            new_pos = ch_tell();
  1401. X            break;
  1402. X        }
  1403. X        if (p >= &linebuf[sizeof(linebuf)-1])
  1404. X        {
  1405. X            /*
  1406. X             * Overflowed the input buffer.
  1407. X             * Pretend the line ended here.
  1408. X             * {{ The line buffer is supposed to be big
  1409. X             *    enough that this never happens. }}
  1410. X             */
  1411. X            new_pos = ch_tell() - 1;
  1412. X            break;
  1413. X        }
  1414. X        *p++ = c;
  1415. X        c = ch_forw_get();
  1416. X    }
  1417. X    *p = '\0';
  1418. X    line = linebuf;
  1419. X    return (new_pos);
  1420. X}
  1421. X
  1422. X/*
  1423. X * Analogous to back_line(), but deals with "raw lines".
  1424. X * {{ This is supposed to be more efficient than back_line(). }}
  1425. X */
  1426. X    public POSITION
  1427. Xback_raw_line(curr_pos)
  1428. X    POSITION curr_pos;
  1429. X{
  1430. X    register char *p;
  1431. X    register int c;
  1432. X    POSITION new_pos;
  1433. X
  1434. X    if (curr_pos == NULL_POSITION || curr_pos <= (POSITION)0 ||
  1435. X        ch_seek(curr_pos-1))
  1436. X        return (NULL_POSITION);
  1437. X
  1438. X    p = &linebuf[sizeof(linebuf)];
  1439. X    *--p = '\0';
  1440. X
  1441. X    for (;;)
  1442. X    {
  1443. X        c = ch_back_get();
  1444. X        if (c == '\n')
  1445. X        {
  1446. X            /*
  1447. X             * This is the newline ending the previous line.
  1448. X             * We have hit the beginning of the line.
  1449. X             */
  1450. X            new_pos = ch_tell() + 1;
  1451. X            break;
  1452. X        }
  1453. X        if (c == EOI)
  1454. X        {
  1455. X            /*
  1456. X             * We have hit the beginning of the file.
  1457. X             * This must be the first line in the file.
  1458. X             * This must, of course, be the beginning of the line.
  1459. X             */
  1460. X            new_pos = (POSITION)0;
  1461. X            break;
  1462. X        }
  1463. X        if (p <= linebuf)
  1464. X        {
  1465. X            /*
  1466. X             * Overflowed the input buffer.
  1467. X             * Pretend the line ended here.
  1468. X             */
  1469. X            new_pos = ch_tell() + 1;
  1470. X            break;
  1471. X        }
  1472. X        *--p = c;
  1473. X    }
  1474. X    line = p;
  1475. X    return (new_pos);
  1476. X}
  1477. END_OF_FILE
  1478. echo shar: Extracting \"signal.c\"
  1479. sed "s/^X//" >'signal.c' <<'END_OF_FILE'
  1480. X/*
  1481. X * Routines dealing with signals.
  1482. X *
  1483. X * A signal usually merely causes a bit to be set in the "signals" word.
  1484. X * At some convenient time, the mainline code checks to see if any
  1485. X * signals need processing by calling psignal().
  1486. X * If we happen to be reading from a file [in iread()] at the time
  1487. X * the signal is received, we call intread to interrupt the iread.
  1488. X */
  1489. X
  1490. X#include "less.h"
  1491. X#include <signal.h>
  1492. X
  1493. X/*
  1494. X * "sigs" contains bits indicating signals which need to be processed.
  1495. X */
  1496. Xpublic int sigs;
  1497. X
  1498. X#define    S_INTERRUPT    01
  1499. X#ifdef SIGTSTP
  1500. X#define    S_STOP        02
  1501. X#endif
  1502. X#if defined(SIGWINCH) || defined(SIGWIND)
  1503. X#define S_WINCH        04
  1504. X#endif
  1505. X
  1506. Xextern int sc_width, sc_height;
  1507. Xextern int screen_trashed;
  1508. Xextern int lnloop;
  1509. Xextern int linenums;
  1510. Xextern int scroll;
  1511. Xextern int reading;
  1512. X
  1513. X/*
  1514. X * Interrupt signal handler.
  1515. X */
  1516. X    static HANDLER
  1517. Xinterrupt()
  1518. X{
  1519. X    SIGNAL(SIGINT, interrupt);
  1520. X    sigs |= S_INTERRUPT;
  1521. X    if (reading)
  1522. X        intread();
  1523. X}
  1524. X
  1525. X#ifdef SIGTSTP
  1526. X/*
  1527. X * "Stop" (^Z) signal handler.
  1528. X */
  1529. X    static HANDLER
  1530. Xstop()
  1531. X{
  1532. X    SIGNAL(SIGTSTP, stop);
  1533. X    sigs |= S_STOP;
  1534. X    if (reading)
  1535. X        intread();
  1536. X}
  1537. X#endif
  1538. X
  1539. X#ifdef SIGWINCH
  1540. X/*
  1541. X * "Window" change handler
  1542. X */
  1543. X    public HANDLER
  1544. Xwinch()
  1545. X{
  1546. X    SIGNAL(SIGWINCH, winch);
  1547. X    sigs |= S_WINCH;
  1548. X    if (reading)
  1549. X        intread();
  1550. X}
  1551. X#else
  1552. X#ifdef SIGWIND
  1553. X/*
  1554. X * "Window" change handler
  1555. X */
  1556. X    public HANDLER
  1557. Xwinch()
  1558. X{
  1559. X    SIGNAL(SIGWIND, winch);
  1560. X    sigs |= S_WINCH;
  1561. X    if (reading)
  1562. X        intread();
  1563. X}
  1564. X#endif
  1565. X#endif
  1566. X
  1567. X/*
  1568. X * Set up the signal handlers.
  1569. X */
  1570. X    public void
  1571. Xinit_signals(on)
  1572. X    int on;
  1573. X{
  1574. X    if (on)
  1575. X    {
  1576. X        /*
  1577. X         * Set signal handlers.
  1578. X         */
  1579. X        (void) SIGNAL(SIGINT, interrupt);
  1580. X#ifdef SIGTSTP
  1581. X        (void) SIGNAL(SIGTSTP, stop);
  1582. X#endif
  1583. X#ifdef SIGWINCH
  1584. X        (void) SIGNAL(SIGWINCH, winch);
  1585. X#else
  1586. X#ifdef SIGWIND
  1587. X        (void) SIGNAL(SIGWIND, winch);
  1588. X#endif
  1589. X#endif
  1590. X    } else
  1591. X    {
  1592. X        /*
  1593. X         * Restore signals to defaults.
  1594. X         */
  1595. X        (void) SIGNAL(SIGINT, SIG_DFL);
  1596. X#ifdef SIGTSTP
  1597. X        (void) SIGNAL(SIGTSTP, SIG_DFL);
  1598. X#endif
  1599. X#ifdef SIGWINCH
  1600. X        (void) SIGNAL(SIGWINCH, SIG_IGN);
  1601. X#endif
  1602. X#ifdef SIGWIND
  1603. X        (void) SIGNAL(SIGWIND, SIG_IGN);
  1604. X#endif
  1605. X    }
  1606. X}
  1607. X
  1608. X/*
  1609. X * Process any signals we have received.
  1610. X * A received signal cause a bit to be set in "sigs".
  1611. X */
  1612. X    public int
  1613. Xpsignals()
  1614. X{
  1615. X    register int tsignals;
  1616. X
  1617. X    if ((tsignals = sigs) == 0)
  1618. X        return (0);
  1619. X    sigs = 0;
  1620. X
  1621. X#ifdef S_WINCH
  1622. X    if (tsignals & S_WINCH)
  1623. X    {
  1624. X        int old_width, old_height;
  1625. X        /*
  1626. X         * Re-execute get_term() to read the new window size.
  1627. X         */
  1628. X        old_width = sc_width;
  1629. X        old_height = sc_height;
  1630. X        get_term();
  1631. X        if (sc_width != old_width || sc_height != old_height)
  1632. X        {
  1633. X            scroll = (sc_height + 1) / 2;
  1634. X            screen_trashed = 1;
  1635. X        }
  1636. X    }
  1637. X#endif
  1638. X#ifdef SIGTSTP
  1639. X    if (tsignals & S_STOP)
  1640. X    {
  1641. X        /*
  1642. X         * Clean up the terminal.
  1643. X         */
  1644. X#ifdef SIGTTOU
  1645. X        SIGNAL(SIGTTOU, SIG_IGN);
  1646. X#endif
  1647. X        lower_left();
  1648. X        clear_eol();
  1649. X        deinit();
  1650. X        flush();
  1651. X        raw_mode(0);
  1652. X#ifdef SIGTTOU
  1653. X        SIGNAL(SIGTTOU, SIG_DFL);
  1654. X#endif
  1655. X        SIGNAL(SIGTSTP, SIG_DFL);
  1656. X        kill(getpid(), SIGTSTP);
  1657. X        /*
  1658. X         * ... Bye bye. ...
  1659. X         * Hopefully we'll be back later and resume here...
  1660. X         * Reset the terminal and arrange to repaint the
  1661. X         * screen when we get back to the main command loop.
  1662. X         */
  1663. X        SIGNAL(SIGTSTP, stop);
  1664. X        raw_mode(1);
  1665. X        init();
  1666. X        screen_trashed = 1;
  1667. X    }
  1668. X#endif
  1669. X    if (tsignals & S_INTERRUPT)
  1670. X    {
  1671. X        bell();
  1672. X        /*
  1673. X         * {{ You may wish to replace the bell() with 
  1674. X         *    error("Interrupt"); }}
  1675. X         */
  1676. X
  1677. X        /*
  1678. X         * If we were interrupted while in the "calculating 
  1679. X         * line numbers" loop, turn off line numbers.
  1680. X         */
  1681. X        if (lnloop)
  1682. X        {
  1683. X            lnloop = 0;
  1684. X            linenums = 0;
  1685. X            error("Line numbers turned off");
  1686. X        }
  1687. X
  1688. X    }
  1689. X
  1690. X    return (1);
  1691. X}
  1692. END_OF_FILE
  1693. echo shar: Extracting \"os.c\"
  1694. sed "s/^X//" >'os.c' <<'END_OF_FILE'
  1695. X/*
  1696. X * Operating system dependent routines.
  1697. X *
  1698. X * Most of the stuff in here is based on Unix, but an attempt
  1699. X * has been made to make things work on other operating systems.
  1700. X * This will sometimes result in a loss of functionality, unless
  1701. X * someone rewrites code specifically for the new operating system.
  1702. X *
  1703. X * The makefile provides defines to decide whether various
  1704. X * Unix features are present.
  1705. X */
  1706. X
  1707. X#include <stdio.h>
  1708. X#include <signal.h>
  1709. X#include <setjmp.h>
  1710. X#include "less.h"
  1711. X
  1712. Xchar *getenv();
  1713. X
  1714. Xpublic int reading;
  1715. X
  1716. Xextern int screen_trashed;
  1717. X
  1718. Xstatic jmp_buf read_label;
  1719. X
  1720. X/*
  1721. X * Pass the specified command to a shell to be executed.
  1722. X * Like plain "system()", but handles resetting terminal modes, etc.
  1723. X */
  1724. X    public void
  1725. Xlsystem(cmd)
  1726. X    char *cmd;
  1727. X{
  1728. X    int inp;
  1729. X    char cmdbuf[256];
  1730. X    char *shell;
  1731. X
  1732. X    /*
  1733. X     * Print the command which is to be executed,
  1734. X     * unless the command starts with a "-".
  1735. X     */
  1736. X    if (cmd[0] == '-')
  1737. X        cmd++;
  1738. X    else
  1739. X    {
  1740. X        lower_left();
  1741. X        clear_eol();
  1742. X        putstr("!");
  1743. X        putstr(cmd);
  1744. X        putstr("\n");
  1745. X    }
  1746. X
  1747. X    /*
  1748. X     * De-initialize the terminal and take out of raw mode.
  1749. X     */
  1750. X    deinit();
  1751. X    flush();
  1752. X    raw_mode(0);
  1753. X
  1754. X    /*
  1755. X     * Restore signals to their defaults.
  1756. X     */
  1757. X    init_signals(0);
  1758. X
  1759. X    /*
  1760. X     * Force standard input to be the terminal, "/dev/tty",
  1761. X     * even if less's standard input is coming from a pipe.
  1762. X     */
  1763. X    inp = dup(0);
  1764. X    close(0);
  1765. X    if (open("/dev/tty", 0) < 0)
  1766. X        dup(inp);
  1767. X
  1768. X    /*
  1769. X     * Pass the command to the system to be executed.
  1770. X     * If we have a SHELL environment variable, use
  1771. X     * <$SHELL -c "command"> instead of just <command>.
  1772. X     * If the command is empty, just invoke a shell.
  1773. X     */
  1774. X    if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
  1775. X    {
  1776. X        if (*cmd == '\0')
  1777. X            cmd = shell;
  1778. X        else
  1779. X        {
  1780. X            sprintf(cmdbuf, "%s -c \"%s\"", shell, cmd);
  1781. X            cmd = cmdbuf;
  1782. X        }
  1783. X    }
  1784. X    if (*cmd == '\0')
  1785. X        cmd = "sh";
  1786. X
  1787. X    system(cmd);
  1788. X
  1789. X    /*
  1790. X     * Restore standard input, reset signals, raw mode, etc.
  1791. X     */
  1792. X    close(0);
  1793. X    dup(inp);
  1794. X    close(inp);
  1795. X
  1796. X    init_signals(1);
  1797. X    raw_mode(1);
  1798. X    init();
  1799. X    screen_trashed = 1;
  1800. X#if defined(SIGWINCH) || defined(SIGWIND)
  1801. X    /*
  1802. X     * Since we were ignoring window change signals while we executed
  1803. X     * the system command, we must assume the window changed.
  1804. X     */
  1805. X    winch();
  1806. X#endif
  1807. X}
  1808. X
  1809. X/*
  1810. X * Like read() system call, but is deliberately interruptable.
  1811. X * A call to intread() from a signal handler will interrupt
  1812. X * any pending iread().
  1813. X */
  1814. X    public int
  1815. Xiread(fd, buf, len)
  1816. X    int fd;
  1817. X    char *buf;
  1818. X    int len;
  1819. X{
  1820. X    register int n;
  1821. X
  1822. X    if (setjmp(read_label))
  1823. X        /*
  1824. X         * We jumped here from intread.
  1825. X         */
  1826. X        return (READ_INTR);
  1827. X
  1828. X    flush();
  1829. X    reading = 1;
  1830. X    n = read(fd, buf, len);
  1831. X    reading = 0;
  1832. X    if (n < 0)
  1833. X        return (-1);
  1834. X    return (n);
  1835. X}
  1836. X
  1837. X    public void
  1838. Xintread()
  1839. X{
  1840. X#if SIGSETMASK
  1841. X    sigsetmask(0);
  1842. X#endif
  1843. X    longjmp(read_label, 1);
  1844. X}
  1845. X
  1846. X#if GET_TIME
  1847. X    public long
  1848. Xget_time()
  1849. X{
  1850. X    long t;
  1851. X
  1852. X    time(&t);
  1853. X    return (t);
  1854. X}
  1855. X#endif
  1856. X
  1857. X/*
  1858. X * Expand a filename, substituting any environment variables, etc.
  1859. X * The implementation of this is necessarily very operating system
  1860. X * dependent.  This implementation is unabashedly only for Unix systems.
  1861. X */
  1862. X#if GLOB
  1863. X
  1864. XFILE *popen();
  1865. X
  1866. X    public char *
  1867. Xglob(filename)
  1868. X    char *filename;
  1869. X{
  1870. X    FILE *f;
  1871. X    char *p;
  1872. X    int ch;
  1873. X    char *cmd;
  1874. X    static char buffer[FILENAME];
  1875. X
  1876. X    if (filename[0] == '#')
  1877. X        return (filename);
  1878. X
  1879. X    /*
  1880. X     * We get the shell to expand the filename for us by passing
  1881. X     * an "echo" command to the shell and reading its output.
  1882. X     */
  1883. X    p = getenv("SHELL");
  1884. X    if (p == NULL || *p == '\0')
  1885. X    {
  1886. X        /*
  1887. X         * Read the output of <echo filename>.
  1888. X         */
  1889. X        cmd = calloc(strlen(filename)+8, sizeof(char));
  1890. X        if (cmd == NULL)
  1891. X            return (filename);
  1892. X        sprintf(cmd, "echo \"%s\"", filename);
  1893. X    } else
  1894. X    {
  1895. X        /*
  1896. X         * Read the output of <$SHELL -c "echo filename">.
  1897. X         */
  1898. X        cmd = calloc(strlen(p)+12);
  1899. X        if (cmd == NULL)
  1900. X            return (filename);
  1901. X        sprintf(cmd, "%s -c \"echo %s\"", p, filename);
  1902. X    }
  1903. X
  1904. X    if ((f = popen(cmd, "r")) == NULL)
  1905. X        return (filename);
  1906. X    free(cmd);
  1907. X
  1908. X    for (p = buffer;  p < &buffer[sizeof(buffer)-1];  p++)
  1909. X    {
  1910. X        if ((ch = getc(f)) == '\n' || ch == EOF)
  1911. X            break;
  1912. X        *p = ch;
  1913. X    }
  1914. X    *p = '\0';
  1915. X    pclose(f);
  1916. X    return (buffer);
  1917. X}
  1918. X
  1919. X#else
  1920. X
  1921. X    public char *
  1922. Xglob(filename)
  1923. X    char *filename;
  1924. X{
  1925. X    return (filename);
  1926. X}
  1927. X
  1928. X#endif
  1929. X
  1930. X
  1931. X/*
  1932. X * Returns NULL if the file can be opened and
  1933. X * is an ordinary file, otherwise an error message
  1934. X * (if it cannot be opened or is a directory, etc.)
  1935. X */
  1936. X
  1937. X#if STAT
  1938. X
  1939. X#include <sys/types.h>
  1940. X#include <sys/stat.h>
  1941. X
  1942. X    public char *
  1943. Xbad_file(filename, message, len)
  1944. X    char *filename;
  1945. X    char *message;
  1946. X    unsigned int len;
  1947. X{
  1948. X    struct stat statbuf;
  1949. X
  1950. X    if (stat(filename, &statbuf) < 0)
  1951. X        return (errno_message(filename, message, len));
  1952. X
  1953. X    if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
  1954. X    {
  1955. X        static char is_dir[] = " is a directory";
  1956. X        strtcpy(message, filename, len-sizeof(is_dir)-1);
  1957. X        strcat(message, is_dir);
  1958. X        return (message);
  1959. X    }
  1960. X    if ((statbuf.st_mode & S_IFMT) != S_IFREG)
  1961. X    {
  1962. X        static char not_reg[] = " is not a regular file";
  1963. X        strtcpy(message, filename, len-sizeof(not_reg)-1);
  1964. X        strcat(message, not_reg);
  1965. X        return (message);
  1966. X    }
  1967. X    return (NULL);
  1968. X}
  1969. X
  1970. X#else
  1971. X
  1972. X    public char *
  1973. Xbad_file(filename, message, len)
  1974. X    char *filename;
  1975. X    char *message;
  1976. X    unsigned int len;
  1977. X{
  1978. X    return (NULL);
  1979. X}
  1980. X
  1981. X#endif
  1982. X
  1983. X/*
  1984. X * errno_message: Return an error message based on the value of "errno".
  1985. X * okreadfail: Return true if the previous failure of a read
  1986. X *    (on the input tty) should be considered ok.
  1987. X */
  1988. X
  1989. X#if PERROR
  1990. X
  1991. Xextern char *sys_errlist[];
  1992. Xextern int sys_nerr;
  1993. Xextern int errno;
  1994. X
  1995. X    public char *
  1996. Xerrno_message(filename, message, len)
  1997. X    char *filename;
  1998. X    char *message;
  1999. X    unsigned int len;
  2000. X{
  2001. X    char *p;
  2002. X    char msg[16];
  2003. X
  2004. X    if (errno < sys_nerr)
  2005. X        p = sys_errlist[errno];
  2006. X    else
  2007. X    {
  2008. X        sprintf(msg, "Error %d", errno);
  2009. X        p = msg;
  2010. X    }
  2011. X    strtcpy(message, filename, len-strlen(p)-3);
  2012. X    strcat(message, ": ");
  2013. X    strcat(message, p);
  2014. X    return (message);
  2015. X}
  2016. X
  2017. X#else
  2018. X
  2019. X    public char *
  2020. Xerrno_message(filename, message, len)
  2021. X    char *filename;
  2022. X    char *message;
  2023. X    unsigned int len;
  2024. X{
  2025. X    static char msg[] = ": cannot open";
  2026. X
  2027. X    strtcpy(message, filename, len-sizeof(msg)-1);
  2028. X    strcat(message, msg);
  2029. X    return (message);
  2030. X}
  2031. X
  2032. X#endif
  2033. END_OF_FILE
  2034. echo shar: Extracting \"help.c\"
  2035. sed "s/^X//" >'help.c' <<'END_OF_FILE'
  2036. X#include  "less.h"
  2037. X
  2038. X/*
  2039. X * Display some help.
  2040. X * Just invoke another "less" to display the help file.
  2041. X *
  2042. X * {{ This makes this function very simple, and makes changing the
  2043. X *    help file very easy, but it may present difficulties on
  2044. X *    (non-Unix) systems which do not supply the "system()" function. }}
  2045. X */
  2046. X
  2047. X    public void
  2048. Xhelp()
  2049. X{
  2050. X    char cmd[FILENAME+100];
  2051. X
  2052. X    sprintf(cmd, 
  2053. X     "-less -m '-PmHELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done ' %s",
  2054. X     HELPFILE);
  2055. X    lsystem(cmd);
  2056. X    error("End of help");
  2057. X}
  2058. END_OF_FILE
  2059. echo shar: Extracting \"ttyin.c\"
  2060. sed "s/^X//" >'ttyin.c' <<'END_OF_FILE'
  2061. X/*
  2062. X * Routines dealing with getting input from the keyboard (i.e. from the user).
  2063. X */
  2064. X
  2065. X#include "less.h"
  2066. X
  2067. Xstatic int tty;
  2068. X
  2069. X/*
  2070. X * Open keyboard for input.
  2071. X * (Just use file descriptor 2.)
  2072. X */
  2073. X    public void
  2074. Xopen_getchr()
  2075. X{
  2076. X    tty = 2;
  2077. X}
  2078. X
  2079. X/*
  2080. X * Get a character from the keyboard.
  2081. X */
  2082. X    public int
  2083. Xgetchr()
  2084. X{
  2085. X    char c;
  2086. X    int result;
  2087. X
  2088. X    do
  2089. X    {
  2090. X        result = iread(tty, &c, 1);
  2091. X        if (result == READ_INTR)
  2092. X            return (READ_INTR);
  2093. X        if (result < 0)
  2094. X        {
  2095. X            /*
  2096. X             * Don't call error() here,
  2097. X             * because error calls getchr!
  2098. X             */
  2099. X            quit();
  2100. X        }
  2101. X    } while (result != 1);
  2102. X    return (c & 0177);
  2103. X}
  2104. END_OF_FILE
  2105. echo shar: Extracting \"command.c\"
  2106. sed "s/^X//" >'command.c' <<'END_OF_FILE'
  2107. X/*
  2108. X * User-level command processor.
  2109. X */
  2110. X
  2111. X#include "less.h"
  2112. X#include "position.h"
  2113. X#include "cmd.h"
  2114. X
  2115. X#define    NO_MCA        0
  2116. X#define    MCA_DONE    1
  2117. X#define    MCA_MORE    2
  2118. X
  2119. Xextern int erase_char, kill_char;
  2120. Xextern int ispipe;
  2121. Xextern int sigs;
  2122. Xextern int quit_at_eof;
  2123. Xextern int hit_eof;
  2124. Xextern int sc_width;
  2125. Xextern int sc_height;
  2126. Xextern int sc_window;
  2127. Xextern int curr_ac;
  2128. Xextern int ac;
  2129. Xextern int quitting;
  2130. Xextern int scroll;
  2131. Xextern char *first_cmd;
  2132. Xextern char *every_first_cmd;
  2133. Xextern char version[];
  2134. Xextern char *current_file;
  2135. X#if EDITOR
  2136. Xextern char *editor;
  2137. X#endif
  2138. Xextern int screen_trashed;    /* The screen has been overwritten */
  2139. X
  2140. Xstatic char cmdbuf[120];    /* Buffer for holding a multi-char command */
  2141. X#if SHELL_ESCAPE
  2142. Xstatic char *shellcmd = NULL;    /* For holding last shell command for "!!" */
  2143. X#endif
  2144. Xstatic char *cp;        /* Pointer into cmdbuf */
  2145. Xstatic int cmd_col;        /* Current column of the multi-char command */
  2146. Xstatic int mca;            /* The multicharacter command (action) */
  2147. Xstatic int last_mca;        /* The previous mca */
  2148. Xstatic int number;        /* The number typed by the user */
  2149. Xstatic int wsearch;        /* Search for matches (1) or non-matches (0) */
  2150. X
  2151. X/*
  2152. X * Reset command buffer (to empty).
  2153. X */
  2154. Xcmd_reset()
  2155. X{
  2156. X    cp = cmdbuf;
  2157. X}
  2158. X
  2159. X/*
  2160. X * Backspace in command buffer.
  2161. X */
  2162. X    static int
  2163. Xcmd_erase()
  2164. X{
  2165. X    if (cp == cmdbuf)
  2166. X        /*
  2167. X         * Backspace past beginning of the string:
  2168. X         * this usually means abort the command.
  2169. X         */
  2170. X        return (1);
  2171. X
  2172. X    if (control_char(*--cp))
  2173. X    {
  2174. X        /*
  2175. X         * Erase an extra character, for the carat.
  2176. X         */
  2177. X        backspace();
  2178. X        cmd_col--;
  2179. X    }
  2180. X    backspace();
  2181. X    cmd_col--;
  2182. X    return (0);
  2183. X}
  2184. X
  2185. X/*
  2186. X * Set up the display to start a new multi-character command.
  2187. X */
  2188. Xstart_mca(action, prompt)
  2189. X    int action;
  2190. X    char *prompt;
  2191. X{
  2192. X    lower_left();
  2193. X    clear_eol();
  2194. X    putstr(prompt);
  2195. X    cmd_col = strlen(prompt);
  2196. X    mca = action;
  2197. X}
  2198. X
  2199. X/*
  2200. X * Process a single character of a multi-character command, such as
  2201. X * a number, or the pattern of a search command.
  2202. X */
  2203. X    static int
  2204. Xcmd_char(c)
  2205. X    int c;
  2206. X{
  2207. X    if (c == erase_char)
  2208. X    {
  2209. X        if (cmd_erase())
  2210. X            return (1);
  2211. X    } else if (c == kill_char)
  2212. X    {
  2213. X        /* {{ Could do this faster, but who cares? }} */
  2214. X        while (cmd_erase() == 0)
  2215. X            ;
  2216. X    } else if (cp >= &cmdbuf[sizeof(cmdbuf)-1])
  2217. X    {
  2218. X        /*
  2219. X         * No room in the command buffer.
  2220. X         */
  2221. X        bell();
  2222. X    } else if (cmd_col >= sc_width-3)
  2223. X    {
  2224. X        /*
  2225. X         * No room on the screen.
  2226. X         * {{ Could get fancy here; maybe shift the displayed
  2227. X         *    line and make room for more chars, like ksh. }}
  2228. X         */
  2229. X        bell();
  2230. X    } else
  2231. X    {
  2232. X        /*
  2233. X         * Append the character to the string.
  2234. X         */
  2235. X        *cp++ = c;
  2236. X        if (control_char(c))
  2237. X        {
  2238. X            putchr('^');
  2239. X            cmd_col++;
  2240. X            c = carat_char(c);
  2241. X        }
  2242. X        putchr(c);
  2243. X        cmd_col++;
  2244. X    }
  2245. X    return (0);
  2246. X}
  2247. X
  2248. X/*
  2249. X * Return the number currently in the command buffer.
  2250. X */
  2251. X    static int
  2252. Xcmd_int()
  2253. X{
  2254. X    *cp = '\0';
  2255. X    cp = cmdbuf;
  2256. X    return (atoi(cmdbuf));
  2257. X}
  2258. X
  2259. X/*
  2260. X * Move the cursor to lower left before executing a command.
  2261. X * This looks nicer if the command takes a long time before
  2262. X * updating the screen.
  2263. X */
  2264. X    static void
  2265. Xcmd_exec()
  2266. X{
  2267. X    lower_left();
  2268. X    flush();
  2269. X}
  2270. X
  2271. X/*
  2272. X * Display the appropriate prompt.
  2273. X */
  2274. X    static void
  2275. Xprompt()
  2276. X{
  2277. X    register char *p;
  2278. X
  2279. X    if (first_cmd != NULL && *first_cmd != '\0')
  2280. X    {
  2281. X        /*
  2282. X         * No prompt necessary if commands are from first_cmd
  2283. X         * rather than from the user.
  2284. X         */
  2285. X        return;
  2286. X    }
  2287. X
  2288. X    /*
  2289. X     * If nothing is displayed yet, display starting from line 1.
  2290. X     */
  2291. X    if (position(TOP) == NULL_POSITION)
  2292. X        jump_back(1);
  2293. X    else if (screen_trashed)
  2294. X        repaint();
  2295. X
  2296. X    /*
  2297. X     * If the -E flag is set and we've hit EOF on the last file, quit.
  2298. X     */
  2299. X    if (quit_at_eof == 2 && hit_eof && curr_ac + 1 >= ac)
  2300. X        quit();
  2301. X
  2302. X    /*
  2303. X     * Select the proper prompt and display it.
  2304. X     */
  2305. X    lower_left();
  2306. X    clear_eol();
  2307. X    p = pr_string();
  2308. X    if (p == NULL)
  2309. X        putchr(':');
  2310. X    else
  2311. X    {
  2312. X        so_enter();
  2313. X        putstr(p);
  2314. X        so_exit();
  2315. X    }
  2316. X}
  2317. X
  2318. X/*
  2319. X * Get command character.
  2320. X * The character normally comes from the keyboard,
  2321. X * but may come from the "first_cmd" string.
  2322. X */
  2323. X    static int
  2324. Xgetcc()
  2325. X{
  2326. X    if (first_cmd == NULL)
  2327. X        return (getchr());
  2328. X
  2329. X    if (*first_cmd == '\0')
  2330. X    {
  2331. X        /*
  2332. X         * Reached end of first_cmd input.
  2333. X         */
  2334. X        first_cmd = NULL;
  2335. X        if (cp > cmdbuf && position(TOP) == NULL_POSITION)
  2336. X        {
  2337. X            /*
  2338. X             * Command is incomplete, so try to complete it.
  2339. X             * There are only two cases:
  2340. X             * 1. We have "/string" but no newline.  Add the \n.
  2341. X             * 2. We have a number but no command.  Treat as #g.
  2342. X             * (This is all pretty hokey.)
  2343. X             */
  2344. X            if (mca != A_DIGIT)
  2345. X                /* Not a number; must be search string */
  2346. X                return ('\n'); 
  2347. X            else
  2348. X                /* A number; append a 'g' */
  2349. X                return ('g');
  2350. X        }
  2351. X        return (getchr());
  2352. X    }
  2353. X    return (*first_cmd++);
  2354. X}
  2355. X
  2356. X/*
  2357. X * Execute a multicharacter command.
  2358. X */
  2359. X    static void
  2360. Xexec_mca()
  2361. X{
  2362. X    register char *p;
  2363. X    register int n;
  2364. X
  2365. X    *cp = '\0';
  2366. X    cmd_exec();
  2367. X    switch (mca)
  2368. X    {
  2369. X    case A_F_SEARCH:
  2370. X        search(1, cmdbuf, number, wsearch);
  2371. X        break;
  2372. X    case A_B_SEARCH:
  2373. X        search(0, cmdbuf, number, wsearch);
  2374. X        break;
  2375. X    case A_FIRSTCMD:
  2376. X        /*
  2377. X         * Skip leading spaces or + signs in the string.
  2378. X         */
  2379. X        for (p = cmdbuf;  *p == '+' || *p == ' ';  p++)
  2380. X            ;
  2381. X        if (every_first_cmd != NULL)
  2382. X            free(every_first_cmd);
  2383. X        if (*p == '\0')
  2384. X            every_first_cmd = NULL;
  2385. X        else
  2386. X            every_first_cmd = save(p);
  2387. X        break;
  2388. X    case A_TOGGLE_OPTION:
  2389. X        toggle_option(cmdbuf, 1);
  2390. X        break;
  2391. X    case A_EXAMINE:
  2392. X        /*
  2393. X         * Ignore leading spaces in the filename.
  2394. X         */
  2395. X        for (p = cmdbuf;  *p == ' ';  p++)
  2396. X            ;
  2397. X        edit(glob(p));
  2398. X        break;
  2399. X#if SHELL_ESCAPE
  2400. X    case A_SHELL:
  2401. X        /*
  2402. X         * !! just uses whatever is in shellcmd.
  2403. X         * Otherwise, copy cmdbuf to shellcmd,
  2404. X         * replacing any '%' with the current
  2405. X         * file name.
  2406. X         */
  2407. X        if (*cmdbuf != '!')
  2408. X        {
  2409. X            register char *fr, *to;
  2410. X
  2411. X            /*
  2412. X             * Make one pass to see how big a buffer we 
  2413. X             * need to allocate for the expanded shell cmd.
  2414. X             */
  2415. X            for (fr = cmdbuf;  *fr != '\0';  fr++)
  2416. X                if (*fr == '%')
  2417. X                    n += strlen(current_file);
  2418. X                else
  2419. X                    n++;
  2420. X
  2421. X            if (shellcmd != NULL)
  2422. X                free(shellcmd);
  2423. X            shellcmd = calloc(n+1, sizeof(char));
  2424. X            if (shellcmd == NULL)
  2425. X            {
  2426. X                error("cannot allocate memory");
  2427. X                break;
  2428. X            }
  2429. X
  2430. X            /*
  2431. X             * Now copy the shell cmd, expanding any "%"
  2432. X             * into the current filename.
  2433. X             */
  2434. X            to = shellcmd;
  2435. X            for (fr = cmdbuf;  *fr != '\0';  fr++)
  2436. X            {
  2437. X                if (*fr != '%')
  2438. X                    *to++ = *fr;
  2439. X                else
  2440. X                {
  2441. X                    strcpy(to, current_file);
  2442. X                    to += strlen(to);
  2443. X                }
  2444. X            }
  2445. X            *to = '\0';
  2446. X        }
  2447. X
  2448. X        if (shellcmd == NULL)
  2449. X            lsystem("");
  2450. X        else
  2451. X            lsystem(shellcmd);
  2452. X        error("!done");
  2453. X        break;
  2454. X#endif
  2455. X    }
  2456. X}
  2457. X
  2458. X/*
  2459. X * Add a character to a multi-character command.
  2460. X */
  2461. X    static int
  2462. Xmca_char(c)
  2463. X    int c;
  2464. X{
  2465. X    switch (mca)
  2466. X    {
  2467. X    case 0:
  2468. X        /*
  2469. X         * Not in a multicharacter command.
  2470. X         */
  2471. X        return (NO_MCA);
  2472. X
  2473. X    case A_PREFIX:
  2474. X        /*
  2475. X         * In the prefix of a command.
  2476. X         */
  2477. X        return (NO_MCA);
  2478. X
  2479. X    case A_DIGIT:
  2480. X        /*
  2481. X         * Entering digits of a number.
  2482. X         * Terminated by a non-digit.
  2483. X         */
  2484. X        if ((c < '0' || c > '9') &&
  2485. X            c != erase_char && c != kill_char)
  2486. X        {
  2487. X            /*
  2488. X             * Not part of the number.
  2489. X             * Treat as a normal command character.
  2490. X             */
  2491. X            number = cmd_int();
  2492. X            mca = 0;
  2493. X            return (NO_MCA);
  2494. X        }
  2495. X        break;
  2496. X
  2497. X    case A_TOGGLE_OPTION:
  2498. X        /*
  2499. X         * Special case for the TOGGLE_OPTION command.
  2500. X         * if the option letter which was entered is a
  2501. X         * single-char option, execute the command immediately,
  2502. X         * so he doesn't have to hit RETURN.
  2503. X         */
  2504. X        if (cp == cmdbuf && c != erase_char && c != kill_char &&
  2505. X            single_char_option(c))
  2506. X        {
  2507. X            cmdbuf[0] = c;
  2508. X            cmdbuf[1] = '\0';
  2509. X            toggle_option(cmdbuf, 1);
  2510. X            return (MCA_DONE);
  2511. X        }
  2512. X        break;
  2513. X    }
  2514. X
  2515. X    /*
  2516. X     * Any other multicharacter command
  2517. X     * is terminated by a newline.
  2518. X     */
  2519. X    if (c == '\n' || c == '\r')
  2520. X    {
  2521. X        /*
  2522. X         * Execute the command.
  2523. X         */
  2524. X        exec_mca();
  2525. X        return (MCA_DONE);
  2526. X    }
  2527. X    /*
  2528. X     * Append the char to the command buffer.
  2529. X     */
  2530. X    if (cmd_char(c))
  2531. X        /*
  2532. X         * Abort the multi-char command.
  2533. X         */
  2534. X        return (MCA_DONE);
  2535. X    /*
  2536. X     * Need another character.
  2537. X     */
  2538. X    return (MCA_MORE);
  2539. X}
  2540. X
  2541. X/*
  2542. X * Main command processor.
  2543. X * Accept and execute commands until a quit command, then return.
  2544. X */
  2545. X    public void
  2546. Xcommands()
  2547. X{
  2548. X    register int c;
  2549. X    register int action;
  2550. X
  2551. X    last_mca = 0;
  2552. X    scroll = (sc_height + 1) / 2;
  2553. X
  2554. X    for (;;)
  2555. X    {
  2556. X        mca = 0;
  2557. X        number = 0;
  2558. X
  2559. X        /*
  2560. X         * See if any signals need processing.
  2561. X         */
  2562. X        if (sigs)
  2563. X        {
  2564. X            psignals();
  2565. X            if (quitting)
  2566. X                quit();
  2567. X        }
  2568. X            
  2569. X        /*
  2570. X         * Display prompt and accept a character.
  2571. X         */
  2572. X        cmd_reset();
  2573. X        prompt();
  2574. X        noprefix();
  2575. X        c = getcc();
  2576. X
  2577. X    again:
  2578. X        if (sigs)
  2579. X            continue;
  2580. X
  2581. X        /*
  2582. X         * If we are in a multicharacter command, call mca_char.
  2583. X         * Otherwise we call cmd_decode to determine the
  2584. X         * action to be performed.
  2585. X         */
  2586. X        if (mca)
  2587. X            switch (mca_char(c))
  2588. X            {
  2589. X            case MCA_MORE:
  2590. X                /*
  2591. X                 * Need another character.
  2592. X                 */
  2593. X                c = getcc();
  2594. X                goto again;
  2595. X            case MCA_DONE:
  2596. X                /*
  2597. X                 * Command has been handled by mca_char.
  2598. X                 * Start clean with a prompt.
  2599. X                 */
  2600. X                continue;
  2601. X            case NO_MCA:
  2602. X                /*
  2603. X                 * Not a multi-char command
  2604. X                 * (at least, not anymore).
  2605. X                 */
  2606. X                break;
  2607. X            }
  2608. X
  2609. X        /*
  2610. X         * Decode the command character and decide what to do.
  2611. X         */
  2612. X        switch (action = cmd_decode(c))
  2613. X        {
  2614. X        case A_DIGIT:
  2615. X            /*
  2616. X             * First digit of a number.
  2617. X             */
  2618. X            start_mca(A_DIGIT, ":");
  2619. X            goto again;
  2620. X
  2621. X        case A_F_SCREEN:
  2622. X            /*
  2623. X             * Forward one screen.
  2624. X             */
  2625. X            if (number <= 0)
  2626. X                number = sc_window;
  2627. X            if (number <= 0)
  2628. X                number = sc_height - 1;
  2629. X            cmd_exec();
  2630. X            forward(number, 1);
  2631. X            break;
  2632. X
  2633. X        case A_B_SCREEN:
  2634. X            /*
  2635. X             * Backward one screen.
  2636. X             */
  2637. X            if (number <= 0)
  2638. X                number = sc_window;
  2639. X            if (number <= 0)
  2640. X                number = sc_height - 1;
  2641. X            cmd_exec();
  2642. X            backward(number, 1);
  2643. X            break;
  2644. X
  2645. X        case A_F_LINE:
  2646. X            /*
  2647. X             * Forward N (default 1) line.
  2648. X             */
  2649. X            if (number <= 0)
  2650. X                number = 1;
  2651. X            cmd_exec();
  2652. X            forward(number, 0);
  2653. X            break;
  2654. X
  2655. X        case A_B_LINE:
  2656. X            /*
  2657. X             * Backward N (default 1) line.
  2658. X             */
  2659. X            if (number <= 0)
  2660. X                number = 1;
  2661. X            cmd_exec();
  2662. X            backward(number, 0);
  2663. X            break;
  2664. X
  2665. X        case A_F_SCROLL:
  2666. X            /*
  2667. X             * Forward N lines 
  2668. X             * (default same as last 'd' or 'u' command).
  2669. X             */
  2670. X            if (number > 0)
  2671. X                scroll = number;
  2672. X            cmd_exec();
  2673. X            forward(scroll, 0);
  2674. X            break;
  2675. X
  2676. X        case A_B_SCROLL:
  2677. X            /*
  2678. X             * Forward N lines 
  2679. X             * (default same as last 'd' or 'u' command).
  2680. X             */
  2681. X            if (number > 0)
  2682. X                scroll = number;
  2683. X            cmd_exec();
  2684. X            backward(scroll, 0);
  2685. X            break;
  2686. X
  2687. X        case A_FREPAINT:
  2688. X            /*
  2689. X             * Flush buffers, then repaint screen.
  2690. X             * Don't flush the buffers on a pipe!
  2691. X             */
  2692. X            if (!ispipe)
  2693. X            {
  2694. X                ch_init(0, 0);
  2695. X                clr_linenum();
  2696. X            }
  2697. X            /* FALLTHRU */
  2698. X        case A_REPAINT:
  2699. X            /*
  2700. X             * Repaint screen.
  2701. X             */
  2702. X            cmd_exec();
  2703. X            repaint();
  2704. X            break;
  2705. X
  2706. X        case A_GOLINE:
  2707. X            /*
  2708. X             * Go to line N, default beginning of file.
  2709. X             */
  2710. X            if (number <= 0)
  2711. X                number = 1;
  2712. X            cmd_exec();
  2713. X            jump_back(number);
  2714. X            break;
  2715. X
  2716. X        case A_PERCENT:
  2717. X            /*
  2718. X             * Go to a specified percentage into the file.
  2719. X             */
  2720. X            if (number < 0)
  2721. X                number = 0;
  2722. X            if (number > 100)
  2723. X                number = 100;
  2724. X            cmd_exec();
  2725. X            jump_percent(number);
  2726. X            break;
  2727. X
  2728. X        case A_GOEND:
  2729. X            /*
  2730. X             * Go to line N, default end of file.
  2731. X             */
  2732. X            cmd_exec();
  2733. X            if (number <= 0)
  2734. X                jump_forw();
  2735. X            else
  2736. X                jump_back(number);
  2737. X            break;
  2738. X
  2739. X        case A_STAT:
  2740. X            /*
  2741. X             * Print file name, etc.
  2742. X             */
  2743. X            cmd_exec();
  2744. X            error(eq_message());
  2745. X            break;
  2746. X            
  2747. X        case A_VERSION:
  2748. X            /*
  2749. X             * Print version number, without the "@(#)".
  2750. X             */
  2751. X            cmd_exec();
  2752. X            error(version+4);
  2753. X            break;
  2754. X
  2755. X        case A_QUIT:
  2756. X            /*
  2757. X             * Exit.
  2758. X             */
  2759. X            quit();
  2760. X
  2761. X        case A_F_SEARCH:
  2762. X        case A_B_SEARCH:
  2763. X            /*
  2764. X             * Search for a pattern.
  2765. X             * Accept chars of the pattern until \n.
  2766. X             */
  2767. X            if (number <= 0)
  2768. X                number = 1;
  2769. X            start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
  2770. X            last_mca = mca;
  2771. X            wsearch = 1;
  2772. X            c = getcc();
  2773. X            if (c == '!')
  2774. X            {
  2775. X                /*
  2776. X                 * Invert the sense of the search.
  2777. X                 * Set wsearch to 0 and get a new
  2778. X                 * character for the start of the pattern.
  2779. X                 */
  2780. X                start_mca(action, 
  2781. X                    (action==A_F_SEARCH) ? "!/" : "!?");
  2782. X                wsearch = 0;
  2783. X                c = getcc();
  2784. X            }
  2785. X            goto again;
  2786. X
  2787. X        case A_AGAIN_SEARCH:
  2788. X            /*
  2789. X             * Repeat previous search.
  2790. X             */
  2791. X            if (number <= 0)
  2792. X                number = 1;
  2793. X            if (wsearch)
  2794. X                start_mca(last_mca, 
  2795. X                    (last_mca==A_F_SEARCH) ? "/" : "?");
  2796. X            else
  2797. X                start_mca(last_mca, 
  2798. X                    (last_mca==A_F_SEARCH) ? "!/" : "!?");
  2799. X            cmd_exec();
  2800. X            search(mca==A_F_SEARCH, (char *)NULL, number, wsearch);
  2801. X            break;
  2802. X
  2803. X        case A_HELP:
  2804. X            /*
  2805. X             * Help.
  2806. X             */
  2807. X            lower_left();
  2808. X            clear_eol();
  2809. X            putstr("help");
  2810. X            cmd_exec();
  2811. X            help();
  2812. X            break;
  2813. X
  2814. X        case A_EXAMINE:
  2815. X            /*
  2816. X             * Edit a new file.  Get the filename.
  2817. X             */
  2818. X            cmd_reset();
  2819. X            start_mca(A_EXAMINE, "Examine: ");
  2820. X            c = getcc();
  2821. X            goto again;
  2822. X            
  2823. X        case A_VISUAL:
  2824. X            /*
  2825. X             * Invoke an editor on the input file.
  2826. X             */
  2827. X#if EDITOR
  2828. X            if (ispipe)
  2829. X            {
  2830. X                error("Cannot edit standard input");
  2831. X                break;
  2832. X            }
  2833. X            /*
  2834. X             * Try to pass the line number to the editor.
  2835. X             */
  2836. X            cmd_exec();
  2837. X            c = currline(MIDDLE);
  2838. X            if (c == 0)
  2839. X                sprintf(cmdbuf, "%s %s",
  2840. X                    editor, current_file);
  2841. X            else
  2842. X                sprintf(cmdbuf, "%s +%d %s",
  2843. X                    editor, c, current_file);
  2844. X            lsystem(cmdbuf);
  2845. X            ch_init(0, 0);
  2846. X            clr_linenum();
  2847. X            break;
  2848. X#else
  2849. X            error("Command not available");
  2850. X            break;
  2851. X#endif
  2852. X
  2853. X        case A_NEXT_FILE:
  2854. X            /*
  2855. X             * Examine next file.
  2856. X             */
  2857. X            if (number <= 0)
  2858. X                number = 1;
  2859. X            next_file(number);
  2860. X            break;
  2861. X
  2862. X        case A_PREV_FILE:
  2863. X            /*
  2864. X             * Examine previous file.
  2865. X             */
  2866. X            if (number <= 0)
  2867. X                number = 1;
  2868. X            prev_file(number);
  2869. X            break;
  2870. X
  2871. X        case A_TOGGLE_OPTION:
  2872. X            /*
  2873. X             * Toggle a flag setting.
  2874. X             */
  2875. X            cmd_reset();
  2876. X            start_mca(A_TOGGLE_OPTION, "-");
  2877. X            c = getcc();
  2878. X            goto again;
  2879. X
  2880. X        case A_DISP_OPTION:
  2881. X            /*
  2882. X             * Report a flag setting.
  2883. X             */
  2884. X            cmd_reset();
  2885. X            start_mca(A_DISP_OPTION, "_");
  2886. X            c = getcc();
  2887. X            if (c == erase_char || c == kill_char)
  2888. X                break;
  2889. X            cmdbuf[0] = c;
  2890. X            cmdbuf[1] = '\0';
  2891. X            toggle_option(cmdbuf, 0);
  2892. X            break;
  2893. X
  2894. X        case A_FIRSTCMD:
  2895. X            /*
  2896. X             * Set an initial command for new files.
  2897. X             */
  2898. X            cmd_reset();
  2899. X            start_mca(A_FIRSTCMD, "+");
  2900. X            c = getcc();
  2901. X            goto again;
  2902. X
  2903. X        case A_SHELL:
  2904. X            /*
  2905. X             * Shell escape.
  2906. X             */
  2907. X#if SHELL_ESCAPE
  2908. X            cmd_reset();
  2909. X            start_mca(A_SHELL, "!");
  2910. X            c = getcc();
  2911. X            goto again;
  2912. X#else
  2913. X            error("Command not available");
  2914. X            break;
  2915. X#endif
  2916. X
  2917. X        case A_SETMARK:
  2918. X            /*
  2919. X             * Set a mark.
  2920. X             */
  2921. X            lower_left();
  2922. X            clear_eol();
  2923. X            start_mca(A_SETMARK, "mark: ");
  2924. X            c = getcc();
  2925. X            if (c == erase_char || c == kill_char)
  2926. X                break;
  2927. X            setmark(c);
  2928. X            break;
  2929. X
  2930. X        case A_GOMARK:
  2931. X            /*
  2932. X             * Go to a mark.
  2933. X             */
  2934. X            lower_left();
  2935. X            clear_eol();
  2936. X            start_mca(A_GOMARK, "goto mark: ");
  2937. X            c = getcc();
  2938. X            if (c == erase_char || c == kill_char)
  2939. X                break;
  2940. X            gomark(c);
  2941. X            break;
  2942. X
  2943. X        case A_PREFIX:
  2944. X            /*
  2945. X             * The command is incomplete (more chars are needed).
  2946. X             * Display the current char so the user knows
  2947. X             * what's going on and get another character.
  2948. X             */
  2949. X            if (mca != A_PREFIX)
  2950. X                start_mca(A_PREFIX, "& ");
  2951. X            if (control_char(c))
  2952. X            {
  2953. X                putchr('^');
  2954. X                c = carat_char(c);
  2955. X            }
  2956. X            putchr(c);
  2957. X            c = getcc();
  2958. X            goto again;
  2959. X
  2960. X        default:
  2961. X            bell();
  2962. X            break;
  2963. X        }
  2964. X    }
  2965. X}
  2966. END_OF_FILE
  2967.  
  2968.  
  2969.